/**
 * \file            mainwindow.cpp
 * \brief           app
 */

/*
 * Copyright (c) 2023 Dino Haw
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * This file is part of GBK-UTF8-Converter.
 *
 * Author:          Dino Haw <347341799@qq.com>
 * Version:         v1.1
 * Change Logs:
 * Date           Author       Notes
 * 2022-12-27     Dino         the first version
 * 2023-01-04     Dino         修复个别 UTF-8 文件会识别成 GBK 文件的问题
 */

#include "mainwindow.h"
#include "./ui_mainwindow.h"

#include <windows.h>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QTextCodec>
#include <QProcess>

#define APP_VERSION     "V1.1"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    setWindowIcon(QIcon(":/icon/icon.png"));
    setWindowTitle("GBK UTF-8 编码转换器");

    QString app_path = QCoreApplication::applicationDirPath();

    ui->lePath->setText(app_path);

    ui->twFileList->setColumnWidth(0, 50);
    ui->twFileList->setColumnWidth(1, 150);
    ui->twFileList->setColumnWidth(2, 400);

    QLabel *labAuther = new QLabel("Design by DinoHaw ", this);
    QLabel *labVersion = new QLabel(" " APP_VERSION " ", this);

    ui->statusBar->addWidget(labAuther);
    ui->statusBar->addWidget(labVersion);
}

MainWindow::~MainWindow()
{
    delete ui;
}


/**
 * @brief 搜索指定路劲下的指定的文件
 * @param file_path 绝对路径
 * @param file_name 需查找的文件名
 * @param file_list 查找到的文件路径列表
 * @param is_recurve 是否递归搜索
 * @return 搜索到的文件列表
 */
int MainWindow::File_Search(const QString &file_path, const QString &file_name, QStringList &file_list, bool is_recurve)
{
    QDir dir(file_path);

    if (dir.exists() == false)
        return -1;

    QStringList name_filter;
    name_filter << file_name;

    dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
    dir.setSorting(QDir::DirsFirst);

    QFileInfoList list = dir.entryInfoList();
    if (list.size() < 1)
        return -1;

    int i = 0;
    do
    {
        QFileInfo file_info = list.at(i);

        if (file_info.isDir() == true && is_recurve == true)
            File_Search(file_info.filePath(), file_name, file_list, is_recurve);
        else
        {
            qDebug() << file_info.filePath() << ":" <<file_info.fileName();
            if (file_info.suffix() == file_name)
            {
                _file_qty ++;
                file_list << file_info.filePath();
            }
        }
        i ++;

    } while (i < list.size());

    return 0;
}


bool MainWindow::Detect_Is_UTF8(QString file_path)
{
    QFile file(file_path);
    QDataStream data_stream(&file);

    file.open(QIODevice::ReadOnly);

    char *raw_data = new char[file.size() + 3];
    memset(raw_data, 0, file.size() + 3);
    int result = data_stream.readRawData(raw_data, file.size());

    qDebug() << "file size: " << file.size();
    qDebug() << "result: " << result;

    for (qint64 i = 0; i < file.size(); i++)
    {
        /* 3 个字节 */
        if ((raw_data[i]   & 0xF0) == 0xE0
        &&  (raw_data[i+1] & 0xC0) == 0x80
        &&  (raw_data[i+2] & 0xC0) == 0x80)
        {
            delete []raw_data;
            return true;
        }
        /* 2 个字节 */
//        else if ((raw_data[i]   & 0xE0) == 0xC0
//        &&       (raw_data[i+1] & 0xC0) == 0x80)
//        {
//            delete []raw_data;
//            return true;
//        }
    }

    delete []raw_data;
    return false;
}


/**
 * @brief 错误判断
 * @return
 */
bool MainWindow::Error_Judge()
{
    if (ui->lePath->text().isEmpty())
    {
        QMessageBox::critical(this, "错误提示", "文件夹路径不能为空");
        return true;
    }

    QDir dir;

    if (dir.exists(ui->lePath->text()) == false)
    {
        QMessageBox::critical(this, "错误提示", "文件夹路径不存在");
        return true;
    }

    if (ui->leFilter->text().isEmpty())
    {
        QMessageBox::critical(this, "错误提示", "文件类型不能为空");
        return true;
    }

    return false;
}


/**
 * @brief 文件错误时的处理
 * @param row 行数，从 0 开始
 */
void MainWindow::File_Error_Handle(size_t row)
{
    QTableWidgetItem *item = new QTableWidgetItem("×");
    QFont font(item->font());
    font.setBold(true);
    font.setPixelSize(20);
    item->setFont(font);
    item->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    ui->twFileList->setItem(row, 0, item);
}


/**
 * @brief “选择文件夹”按钮被按下
 */
void MainWindow::on_btnPath_clicked()
{
    QString open_path;
    QString app_path = QCoreApplication::applicationDirPath();

    if (ui->lePath->text().isEmpty())
        open_path = app_path;
    else
        open_path = ui->lePath->text();

    open_path = QFileDialog::getExistingDirectory(this, "选择文件夹", open_path, QFileDialog::ShowDirsOnly);

    if (open_path.isEmpty() == false)
    {
        ui->lePath->setText(open_path);
    }

    _is_searched = false;
}


/**
 * @brief “开始搜索”按钮被按下
 */
void MainWindow::on_btnSearch_clicked()
{
    if (Error_Judge() == true)
        return;

    if (_is_searched)
        return;

    /* 1. 拆解出所需要的文件类型 */
    _file_filter = ui->leFilter->text().trimmed().split(",");

    foreach (QString str, _file_filter)
        qDebug() << "_file_filter: " << str;

    /* 2. 查找所需要的文件类型 */
    _file_list.clear();
    foreach (QString str, _file_filter)
    {
        File_Search(ui->lePath->text(), str, _file_list, ui->cbRecurve->isChecked());
    }

    /* 3. 将文件名和路径填入 table widget 中 */
    if (_file_list.isEmpty() == false)
    {
        ui->twFileList->clearContents();
        ui->twFileList->setRowCount(_file_list.count());
        ui->twFileList->setAlternatingRowColors(true);

        size_t count = 0;
        QFileInfo file_info;

        foreach (QString path, _file_list)
        {
            file_info.setFile(path);
            QString file_path = file_info.filePath().mid(ui->lePath->text().count(), file_info.filePath().count() - ui->lePath->text().count());

            QTableWidgetItem *item1 = new QTableWidgetItem(file_info.fileName());
            QTableWidgetItem *item2 = new QTableWidgetItem(file_path);
            ui->twFileList->setItem(count, 1, item1);
            ui->twFileList->setItem(count, 2, item2);
            count ++;
        }

        ui->labFileList->setText(QString::asprintf("搜索到的文件列表： (%lld)", _file_list.count()));
    }
    else
    {
        QMessageBox::warning(this, "结果提示", QString::asprintf("未找到后缀为 %s 的文件", ui->leFilter->text().toLatin1().data()));
    }

    _is_searched = true;
}


/**
 * @brief “开始转换”按钮被按下
 */
void MainWindow::on_btnStart_clicked()
{
    on_btnSearch_clicked();

    bool is_failed = false;
    size_t row = 0;
    size_t failed_count = 0;
    QString text;
    QString failed_file;
    QByteArray byte_arr;
    QTextCodec *gbk  = QTextCodec::codecForName("GBK");
    QTextCodec *utf8 = QTextCodec::codecForName("UTF-8");
    QTableWidgetItem *item;

    /* GBK -> UTF-8 */
    if (ui->rbG_U->isChecked())
    {
        foreach (QString path, _file_list)
        {
            QFile file(path);
            QTextCodec::ConverterState conv_sta;
            row ++;

            if (file.open(QIODevice::ReadWrite) == false)
            {
                File_Error_Handle(row-1);
                is_failed = true;
                failed_count ++;
                failed_file += (path + "\r\n");
                continue;
            }

            if (Detect_Is_UTF8(path) == true)
            {
                File_Error_Handle(row-1);
                failed_count ++;
                qDebug() << path << " is UTF8 file";
                file.close();
                continue;
            }

            text = gbk->toUnicode(file.readAll(), file.size(), &conv_sta);
            if (conv_sta.invalidChars)
            {
                File_Error_Handle(row-1);
                failed_count ++;
                qDebug() << path << " is not GBK file";
                file.close();
                continue;
            }

            byte_arr = utf8->fromUnicode(text);

            file.close();
            if (file.open(QIODevice::ReadWrite| QIODevice::Truncate) == false)
            {
                File_Error_Handle(row-1);
                is_failed = true;
                failed_count ++;
                failed_file += (path + "\r\n");
                continue;
            }

            file.write(byte_arr.data(), byte_arr.count());

            file.close();

            if (ui->twFileList->item(row-1, 0) == nullptr)
            {
                item = new QTableWidgetItem(" ");
                ui->twFileList->setItem(row-1, 0, item);
            }
            else
            {
                item = ui->twFileList->item(row-1, 0);
                item->setText(" ");
                ui->twFileList->setItem(row-1, 0, item);
            }
        }
    }
    /* UTF-8 -> GBK */
    else if (ui->rbU_G->isChecked())
    {
        foreach (QString path, _file_list)
        {
            QFile file(path);
            QTextCodec::ConverterState conv_sta;
            row ++;

            if (file.open(QIODevice::ReadWrite) == false)
            {
                File_Error_Handle(row-1);
                is_failed = true;
                failed_count ++;
                failed_file += (path + "\r\n");
                continue;
            }

            if (Detect_Is_UTF8(path) == false)
            {
                File_Error_Handle(row-1);
                failed_count ++;
                qDebug() << path << " is not UTF8 file";
                file.close();
                continue;
            }

            text = utf8->toUnicode(file.readAll(), file.size(), &conv_sta);
            if (conv_sta.invalidChars)
            {
                File_Error_Handle(row-1);
                failed_count ++;
                qDebug() << path << " is not UTF-8 file";
                file.close();
                continue;
            }

            byte_arr = gbk->fromUnicode(text);

            file.close();
            if (file.open(QIODevice::ReadWrite| QIODevice::Truncate) == false)
            {
                File_Error_Handle(row-1);
                is_failed = true;
                failed_count ++;
                failed_file += (path + "\r\n");
                continue;
            }

            file.write(byte_arr.data(), byte_arr.count());

            file.close();

            if (ui->twFileList->item(row-1, 0) == nullptr)
            {
                item = new QTableWidgetItem(" ");
                ui->twFileList->setItem(row-1, 0, item);
            }
            else
            {
                item = ui->twFileList->item(row-1, 0);
                item->setText(" ");
                ui->twFileList->setItem(row-1, 0, item);
            }
        }
    }

    if (is_failed)
    {
        QMessageBox::warning(this, "以下的文件打开失败", failed_file);
    }
    else
    {
        QMessageBox::information(this, "转换结果", QString::asprintf("成功：%lld\r\n失败：%lld", _file_list.count() - failed_count, failed_count));
    }
}


/**
 * @brief 当选择文件夹的路径被修改时
 * @param arg1
 */
void MainWindow::on_lePath_textChanged(const QString &arg1)
{
    Q_UNUSED(arg1);
    _is_searched = false;
}


/**
 * @brief UTF-8 转 GBK 选项发生变化时
 * @param checked
 */
void MainWindow::on_rbU_G_toggled(bool checked)
{
    Q_UNUSED(checked);
    _is_searched = false;
}


/**
 * @brief 文件类型文本发生改变时
 * @param arg1
 */
void MainWindow::on_leFilter_textChanged(const QString &arg1)
{
    Q_UNUSED(arg1);
    _is_searched = false;
}


/**
 * @brief 搜索到的文件列表被双击时
 * @param row 行
 * @param column 列
 */
void MainWindow::on_twFileList_cellDoubleClicked(int row, int column)
{
    if (column == 2)
    {
        QString path = _file_list.at(row);

        path.replace("/", "\\");
        qDebug() << "path: " << path;
        QProcess::startDetached("explorer", QStringList() << QString("/select,") << QString("%1").arg(path));
    }
}

